Skip to main content

ZigTUI

A fast, allocation-free TUI library for Zig

zig fetch --save git+https://github.com/adxdits/zigtui.git
Zig 0.15+License MITPlatforms

Cell-Based Diffing

Only redraws cells that actually changed your terminal stays flicker-free even on fast refresh loops.

14+ Widgets

Block, Paragraph, List, Gauge, Table, Tabs, Sparkline, BarChart, TextInput, Spinner, Tree, Canvas, Popup, Dialog all ready to use.

Mouse Support

Full SGR mouse events on Unix and the native Console API on Windows. Click, scroll, and drag just work.

15 Built-in Themes

Nord, Dracula, Gruvbox, Catppuccin, Tokyo Night and more. Hot-swap themes at runtime with a single line.

Kitty Graphics

Display BMP images in Kitty, WezTerm and foot via the Kitty Graphics Protocol. Unicode-block fallback for other terminals.

No Hidden Allocations

All allocations go through the allocator you supply. Stack buffers, comptime sizing you stay in control of memory.

Minimal example

const std = @import("std");
const tui = @import("zigtui");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    var backend = try tui.backend.init(gpa.allocator());
    defer backend.deinit();

    var terminal = try tui.terminal.Terminal.init(gpa.allocator(), backend.interface());
    defer terminal.deinit();

    try terminal.hideCursor();
    defer terminal.showCursor() catch {};

    var running = true;
    while (running) {
        const event = try backend.interface().pollEvent(100);
        if (event == .key) {
            const k = event.key.code;
            if (k == .esc or (k == .char and k.char == 'q'))
                running = false;
        }

        try terminal.draw({}, struct {
            fn render(_: void, buf: *tui.render.Buffer) !void {
                tui.widgets.Block{
                    .title = "Hello ZigTUI press 'q' to quit",
                    .borders = tui.widgets.Borders.all(),
                    .border_style = .{ .fg = .cyan },
                }.render(buf.getArea(), buf);
            }
        }.render);
    }
}